package org.esky.zip;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;

import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import org.apache.tools.zip.ZipOutputStream;

/**
 * @author Jack.Xu
 * @ClassName: ZipUtils
 * @Description: TODO
 * @author: Jack.Xu
 * @date: 2013年10月21日 下午6:03:41
 */
public class ZipUtils {
	/** 默认编码方式 */
	private static final Charset CHARSET = Charset.forName("GBK");
	/** 默认压缩文件后缀名 */
	private static final String ZIP  = ".zip";
	/** 文件分隔符 */
	private static final String SEPARATOR = "/";
	/** 缓冲大小 */
	private static final int BUFFER = 1024;
	/** 根目录 */
	private static final String BASE_DIR = "";
	/** 压缩包是否存在 */
	private static boolean zipExists = false;
	/** 压缩包输出流 */
	private	static ZipOutputStream out = null;
	
	/**
	 * @Title: zip
	 * @Description: 普通压缩
	 * @param inputPaths 输入文件（目录）路径
	 * @param zipFilePath 输出压缩文件路径
	 * @throws Throwable
	 * @return: void
	 */
	public static void zip(String[] inputPaths, String zipFilePath) throws Throwable{
		List<File> files = new ArrayList<File>();
		for (String inputPath : inputPaths) {
			File file = new File(inputPath);
			files.add(file);
		}
		compress(files, zipFilePath);
	}
	
	/**
	 * @Title: zip
	 * @Description: 普通压缩
	 * @param files 输入文件（目录）
	 * @param zipFilePath 输出压缩文件路径
	 * @throws Throwable
	 * @return: void
	 */
	public static void zip(File[] files, String zipFilePath) throws Throwable{
		compress(Arrays.asList(files), zipFilePath);
	}

	/**
	 * @Title: compress
	 * @Description: 压缩
	 * @param inputPaths 输入文件（目录）路径
	 * @param zipFilePath 输出压缩文件路径
	 * @throws Throwable
	 * @return: void
	 */
	private static void compress(List<File> files, String zipFilePath) throws Throwable {
		// 创建压缩包文件
		createZipFile(zipFilePath);
		// 递归压缩文件
		for (File inputFile : files) {
			// 输出压缩文件路径校验
			if (!inputFile.exists()) {
				System.out.println("错误      " + inputFile.getPath() + "      不存在");
				continue;
			}
			zip(inputFile, BASE_DIR);
		}
		out.flush();
		out.close();
		System.out.println("完成");
	}
	
	/**
	 * @Title: zip
	 * @Description: 递归压缩  
	 * @param inputFile
	 * @param basePath
	 * @throws Throwable
	 * @return: void
	 */
	private static void zip(File inputFile, String basePath) throws Throwable{
		// 判断是目录还是文件
		if (inputFile.isDirectory()) {
			zipDir(inputFile, basePath);
		}else {
			zipFile(inputFile, basePath);
		}
	}
	
	/**
	 * @Title: zipFile
	 * @Description: 压缩文件
	 * @param inputFile 被压缩的文件
	 * @param basePath 
	 * @throws Throwable
	 * @return: void
	 */
	private static void zipFile(File file, String basePath) throws Throwable{
		ZipEntry entry = new ZipEntry(basePath + file.getName());
		entry.setSize(file.length());
		out.putNextEntry(entry);// 将压缩实体放入输出流
		if (zipExists) {
			System.out.println("更新      " + file.getPath());
		}else {
			System.out.println("添加      " + file.getPath());
		}
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); 
		int len;
		byte[] buf = new byte[BUFFER];
		while ((len = bis.read(buf, 0, BUFFER)) != -1) {
			out.write(buf, 0, len); // 写入到压缩包
			out.flush();
		}
		bis.close();
		out.closeEntry();
		
	}
	
	/**
	 * @Title: zipDir
	 * @Description: 压缩目录
	 * @param inputFile
	 * @param basePath
	 * @throws Throwable
	 * @return: void
	 */
	private static void zipDir(File dir, String basePath) throws Throwable{
		File[] files = dir.listFiles();
		if (files.length == 0) {// 说明这是一个空目录
			out.putNextEntry(new ZipEntry(basePath + dir.getName() + SEPARATOR));
			if (zipExists) {
				System.out.println("更新      " + dir.getPath());
			}else {
				System.out.println("添加      " + dir.getPath());
			}
			out.closeEntry();
		}
		for (File file : files) {
			zip(file, basePath + dir.getName() + SEPARATOR);
		}
	}
	
	/**
	 * @Title: createZipFile
	 * @Description: 创建压缩包文件
	 * @param filePath
	 * @throws Throwable
	 * @return: void
	 */
	private static void createZipFile(String zipFilePath) throws Throwable{
		String ext = zipFilePath.substring(zipFilePath.lastIndexOf("."), zipFilePath.length());
		zipFilePath = zipFilePath.replace(ext, ZIP);
		File zipFile = new File(zipFilePath);
		if (zipFile.exists()) {
			zipExists = true;
			System.out.println("正在升级   压缩包   " + zipFile.getPath() + "\n");
		} else {
			System.out.println("正在创建   压缩包   " + zipFile.getPath() + "\n");
		}
		// 对输出文件做CRC32校验  
		out = new ZipOutputStream(new CheckedOutputStream(new FileOutputStream(zipFile), new CRC32()));
		// 设置压缩编码.设置为GBK后在windows下就不会乱码了，如果要放到Linux或者Unix下就不要设置了
		String osName = System.getProperty("os.name"); // 操作系统名称
		if (osName.toLowerCase().indexOf("windows") >= 0) {
			out.setEncoding(CHARSET.toString());
		}
	}
	
	/**
	 * @Title: unZip
	 * @Description: 解压到当前文件夹
	 * @param zipFilePath 压缩包文件
	 * @throws Throwable
	 * @return: void
	 */
	public static void unZipCurrentDir(String zipFilePath) throws Throwable{
		File zipFile = new File(zipFilePath);
		if (!zipFile.exists()) {
			System.out.println("错误      " + zipFilePath + "      不存在");
			return;
		}
		String currentDir = zipFile.getParent();
		unZip(zipFilePath, currentDir);
	}

	/**
	 * @Title: unZip
	 * @Description: 快捷解压
	 * @param zipFilePath 压缩包文件
	 * @return: void
	 * @throws Throwable 
	 */
	public static void unZip(String zipFilePath) throws Throwable {
		File zipFile = new File(zipFilePath);
		if (!zipFile.exists()) {
			System.out.println("错误      " + zipFilePath + "      不存在");
			return;
		}
		String tmpName = zipFile.getName().replaceAll("\\.+(\\w+)$", ".$1");
		String dir = tmpName.substring(0, tmpName.indexOf("."));
		String outputPath = zipFile.getParent() + SEPARATOR + dir;
		unZip(zipFilePath, outputPath);
	}
	
	/**
	 * @Title: unZip 
	 * @Description: 普通解压
	 * @param zipFilePath 压缩文件路径
	 * @param outputPath 输出文件路径
	 * @throws Throwable
	 * @return: void
	 */
	public static void unZip(String zipFilePath, String outputPath) throws Throwable {
		unCompress(zipFilePath, outputPath);
	}
	
	/**
	 * @Title: unCompress
	 * @Description: 解压缩文件
	 * @param zipFilePath 被解压缩文件路径
	 * @param outputPath 解压出文件路径
	 * @throws Throwable
	 * @return: void
	 */
	private static void unCompress(String zipFilePath, String outputPath) throws Throwable {
		// 获取压缩文件
		ZipFile zipFile = null;
		try {
			zipFile = new ZipFile(zipFilePath, CHARSET.toString());
		} catch (FileNotFoundException e) {
			System.out.println("错误      " + zipFilePath + "      不存在");
			return;
		}
		System.out.println("正在解压   压缩包   " + zipFilePath + "\n");
		outputPath = outputPath + SEPARATOR;
		Enumeration<?> enumeration = zipFile.getEntries();
		while (enumeration.hasMoreElements()) {
			// 获取压缩实体
			ZipEntry entry = (ZipEntry) enumeration.nextElement();
			if (entry.isDirectory()) {
				File dir = new File(outputPath + entry.getName());
				if (!dir.exists()) {
					dir.mkdirs();
					System.out.println("解压      " + dir.getPath());
				}
				continue;
			}
			BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry));
			File outputFile = new File(outputPath + entry.getName());
			File parent = outputFile.getParentFile();
			if (parent != null && (!parent.exists())) {
				parent.mkdirs();
				System.out.println("解压      " + parent.getPath());
			}
			BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outputFile), BUFFER);
			byte[] buf = new byte[BUFFER];
			int len = 0;
			while ((len = bis.read(buf, 0, BUFFER)) != -1) {
				bos.write(buf, 0, len);
			}
			bos.flush();
			bos.close();
			bis.close();
			System.out.println("解压      " + outputFile.getPath());
		}
		zipFile.close();
		System.out.println("完成");
	}
}
